﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml;
using Kailua.WSSLists;

namespace Kailua.net.windward.utils.ado.SPList.custom
{
    /// <summary>
    /// This class is a wrapper for SharePoint's Lists web service. To use it, create an instance with
    /// the constructor, and then call the GetListItems method when you are ready to retrieve the XML.
    /// The constructor is an augmented version of SharePoint's Lists.GetListItems web service method.
    /// Please see the SharePoint documentation to understand this method.
    /// </summary>
    public class SPListWebServiceAdapter
    {
        /// <summary>
        /// The url of the Lists web service.
        /// </summary>
        public string serviceUrl;
        /// <summary>
        /// The NetworkCredential to use to connect to the web service.
        /// </summary>
        public NetworkCredential credentials;
        /// <summary>
        /// The list guid.
        /// </summary>
        public string sListGuid;
        /// <summary>
        /// The view guid.
        /// </summary>
        public string sViewGuid;
        /// <summary>
        /// The CAML query.
        /// </summary>
        public XmlNode query;
        /// <summary>
        /// The ViewFields element.
        /// </summary>
        public XmlNode viewFields;
        /// <summary>
        /// The row limit per page.  This is the number of records returned on each call.
        /// </summary>
        public string rowLimit;
        /// <summary>
        /// Thew QueryOptions element.  The GetListItems method automatically pages through the entire result,
        /// updating the QueryOptions/Paging element.
        /// </summary>
        public XmlNode queryOptions;
        /// <summary>
        /// The web guid.
        /// </summary>
        public string sWebID;

        /// <summary>
        /// Creates a new SPListWebServiceAdapter.
        /// </summary>
        public SPListWebServiceAdapter()
        {

        }

        /// <summary>
        /// The constructor is an augmented version of SharePoint's Lists.GetListItems web service method.
        /// Please see the SharePoint documentation to understand this method.
        /// </summary>
        /// <param name="serviceUrl"></param>
        /// <param name="credentials"></param>
        /// <param name="sListGuid"></param>
        /// <param name="sViewGuid"></param>
        /// <param name="query"></param>
        /// <param name="viewFields"></param>
        /// <param name="rowLimit"></param>
        /// <param name="queryOptions"></param>
        /// <param name="sWebID"></param>
        public SPListWebServiceAdapter(string serviceUrl, NetworkCredential credentials, string sListGuid,
            string sViewGuid, XmlNode query, XmlNode viewFields, string rowLimit, XmlNode queryOptions,
            string sWebID)
        {
            this.serviceUrl = serviceUrl;
            this.credentials = credentials;
            this.sListGuid = sListGuid;
            this.sViewGuid = sViewGuid;
            this.query = query;
            this.viewFields = viewFields;
            this.rowLimit = rowLimit;
            this.queryOptions = queryOptions;
            this.sWebID = sWebID;
        }

        /// <summary>
        /// Calls the Lists.GetListItems web service method with the parameters assigned when
        /// the SPListWebService was created.  Automatically pages through the entire result
        /// returning a single listitems document with all rows merged into listitems/rs:data.
        /// </summary>
        /// <returns></returns>
        public XmlNode GetListItems()
        {
            XmlNode singlePage = GetListItemsSinglePage();

            XmlNode bookmarkNode = getBookMarkNode(singlePage);

            if (bookmarkNode == null) // no more pages of data
                return singlePage;

            // load allListItems into XmlDocument for manipulation
            XmlDocument docAllItems = new XmlDocument();
            docAllItems.LoadXml(singlePage.OuterXml);
            XmlNamespaceManager nsmgrAll = new XmlNamespaceManager(docAllItems.NameTable);
            nsmgrAll.AddNamespace("df", "http://schemas.microsoft.com/sharepoint/soap/"); //default namespace
            nsmgrAll.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
            XmlElement ndDataAll = (XmlElement)docAllItems.SelectSingleNode("/df:listitems/rs:data", nsmgrAll);

            // get the rest of the pages
            while (bookmarkNode != null && queryOptions != null & !string.IsNullOrEmpty(queryOptions.OuterXml))
            {
                // update queryOptions with bookmark (Paging element)
                XmlDocument docQueryOps = new XmlDocument();
                docQueryOps.LoadXml(queryOptions.OuterXml);
                XmlNode ndQueryOps = docQueryOps.SelectSingleNode("/QueryOptions");
                XmlElement ndPaging = (XmlElement)ndQueryOps.SelectSingleNode("/QueryOptions/Paging");
                if (ndPaging == null)
                {
                    ndPaging = docQueryOps.CreateElement("Paging");
                    ndQueryOps.AppendChild(ndPaging);
                }
                ndPaging.SetAttribute("ListItemCollectionPositionNext", bookmarkNode.Value);
                queryOptions = docQueryOps;
                
                // get the next page
                singlePage = GetListItemsSinglePage();

                // add the new items
                XmlDocument docNewPage = new XmlDocument();
                docNewPage.LoadXml(singlePage.OuterXml);
                XmlNamespaceManager nsmgrNewPage = new XmlNamespaceManager(docNewPage.NameTable);
                nsmgrNewPage.AddNamespace("df", "http://schemas.microsoft.com/sharepoint/soap/"); //default namespace
                nsmgrNewPage.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
                
                XmlElement ndDataNewPage = (XmlElement)docNewPage.SelectSingleNode("/df:listitems/rs:data", nsmgrNewPage);
                if (ndDataNewPage == null || !ndDataNewPage.HasChildNodes)
                    return docAllItems;

                foreach (XmlNode ndNewItem in ndDataNewPage.ChildNodes)
                    ndDataAll.AppendChild(docAllItems.ImportNode(ndNewItem, true));

                // update bookmark
                bookmarkNode = getBookMarkNode(singlePage);
            }

            return docAllItems;
        }
        /// <summary>
        /// Makes a single call to Lists.GetListItems and returns the result.  This is done
        /// using the current values of sListGuid, sViewGuid, query, viewFields, rowLimit,
        /// queryOptions, and sWebID.
        /// </summary>
        /// <returns></returns>
        private XmlNode GetListItemsSinglePage()
        {
            Lists listsService = new Lists();
            listsService.Url = serviceUrl;
            listsService.Credentials = credentials;
            return listsService.GetListItems(
                    sListGuid,
                    sViewGuid,
                    query,
                    viewFields,
                    rowLimit,
                    queryOptions,
                    sWebID);
        }
        /// <summary>
        /// Gets the server-supplied bookmark from the specified Lists.GetListItems result.
        /// This is the attribute listitems/rs:data/@ListItemCollectionPositionNext.
        /// </summary>
        /// <param name="listResult"></param>
        /// <returns></returns>
        private XmlNode getBookMarkNode(XmlNode listResult)
        {
            XmlDocument docListResult = new XmlDocument();
            docListResult.LoadXml(listResult.OuterXml);
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(docListResult.NameTable);
            nsmgr.AddNamespace("df", "http://schemas.microsoft.com/sharepoint/soap/"); //default namespace
            nsmgr.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
            return docListResult.SelectSingleNode("/df:listitems/rs:data/@ListItemCollectionPositionNext", nsmgr);
        }
    }
}
